import sys
import os
import StringIO
import datetime
global cur_line, cur_line_pointer, pgmLines
EOF = ""
cur_line_pointer = 0

# Read the next character
def openProgram(self,pgmName):
        global cur_line, cur_line_pointer, pgmLines
        try:
          pgmLines = open(pgmName, 'r')
          cur_line = pgmLines.readline()
          cur_line_pointer = 0
        except IOError:
            print("Error opening program file: " % pgmName)
            return []

def find_line():
    if pgmLines:
        return pgmLines.readline()

# Get full number 
def find_num():
    global cur_line, cur_line_pointer
    digits = []
    previous_dec = False
    last_char = ''
    cur_char = cur_line[cur_line_pointer]
    cur_line_pointer +=1
    if cur_char == '+' or cur_char == '-':
        digits.append(cur_char)
        cur_char = find_char()
    while cur_char.isdigit() or cur_char == '.':
        if cur_char.isdigit and cur_char != '.':
            if last_char == '.':
                digits.append(last_char)
                last_char = ''
            digits.append(cur_char)
            cur_char = cur_line[cur_line_pointer]
            cur_line_pointer +=1
        elif cur_char == '.' and previous_dec == False:
            last_char = '.'
            previous_dec = True
            cur_char = cur_line[cur_line_pointer]
            cur_line_pointer +=1
        elif cur_char == '.':
            cur_line_pointer +=-1
            cur_char = ''
    if cur_char != EOF: 
        cur_line_pointer +=-1
    if last_char == '.':
        cur_line_pointer +=-1
    num = "".join(digits)
    return num

# Get reserved words
def find_reserved():
    global cur_line, cur_line_pointer
    chars = []
    cur_char = cur_line[cur_line_pointer]
    cur_line_pointer +=1
    while cur_char.isalpha():
        chars.append(cur_char)
        cur_char = cur_line[cur_line_pointer]
        cur_line_pointer +=1    
    if cur_char != EOF: 
        cur_line_pointer +=-1
    s = "".join(chars)
    return s

# Get literals
def find_literal():
    global cur_line, cur_line_pointer
    chars = []
    cur_line_pointer +=1
    cur_char = cur_line[cur_line_pointer]
    while cur_char != '\"' and cur_char != EOF:
        chars.append(cur_char)
        cur_char = cur_line[cur_line_pointer]
        cur_line_pointer +=1
    s = "".join(chars)
    return s

# Get identifiers
def find_identifier():
    global cur_line, cur_line_pointer
    chars = []
    cur_char = cur_line[cur_line_pointer]
    cur_line_pointer +=1
    while cur_char.isalnum() or cur_char == '_':
        chars.append(cur_char)
        cur_char = cur_line[cur_line_pointer]
        cur_line_pointer +=1
    if cur_char != EOF: 
        cur_line_pointer +=-1
    s = "".join(chars)
    return s

class Token(object):
    def __init__(self, cur_char, value='', tag=''):
        self.token_code = cur_char
        self.value = value
        self.tag = tag
        
class Num(Token):
    def __init__(self, v):
        Token.__init__(self, 48, v)
      
class Word(Token):
    def __init__(self, token_code, lexeme,tag):
        Token.__init__(self, token_code, lexeme,tag)

class Scanner(object):
    def __init__(self):
        self.lineno = 0
        self.oldline = -1
        self.lookahead = None
        self.not_comment = True
        self.single_not_comment = True
        self.out = []
        self.setting_flags = False
        self.flag1 = True
        self.flag2 = False
        self.flag3 = False
        self.flag4 = False
        self.flag5 = False
        self.flag6 = False
        self.flag7 = False
        self.flag8 = False
        self.flag9 = False
        self.flag10 = False
        self.flag11 = False
        self.flag12 = False
        self.flag13 = False
        self.flag14 = False
        self.flag15 = False
        self.flag16 = False
        self.flag17 = False
        self.flag18 = False
        self.flag19 = False
        self.token_table = {} 
        self.insert("End", 1)
        self.insert("Program", 2)
        self.insert("Declare", 3)
        self.insert("Real", 4)
        self.insert("Integer", 5)
        self.insert("Procedure", 6)
        self.insert("Value", 7)
        self.insert("Reference", 8)
        self.insert("Main", 9)
        self.insert("Input", 10)
        self.insert("Output", 11)
        self.insert("Call", 12)
        self.insert("Else", 13)
        self.insert("If", 14)
        self.insert("Then", 15)
        self.insert("Do", 16)
        self.insert("Until", 17)
        self.insert("+", 45)
        self.insert(";", 22)
        self.insert("=", 41)
        self.insert(",", 23)
        self.insert("[", 24)
        self.insert("]", 25)
        self.insert("(", 26)
        self.insert(")", 27)
        self.insert("<", 38)
        self.insert(">", 36)
        self.insert("!", 39)
        self.insert(":", 20)
        self.insert("-", 46)
        self.insert("*", 37)
        self.insert("/", 44)
        self.insert("{", 49)
        self.insert("}", 50)
        self.insert("<-", 33)
        self.insert("||", 42)
        self.insert("::", 28)
        self.insert("==", 40)
        self.insert("<=", 32)
        self.insert(">=", 35)
        self.insert("&&", 43)
        self.insert("!=", 52)

    def findFlag(self, flag):
        if flag == 15 and self.flag15: return True
     
    def lookup(self, key):
        return self.token_table.get(key, None)

    def insert(self, key, token_code):
        self.token_table[key] = token_code
 
# Get the token
    def getNextToken(self):
        global cur_line_pointer, cur_line
        if cur_line_pointer == 0:
            self.lineno += 1                # increment lineno
        token = None
        token_code = ''
        next_next_char = ''
        while not token \
            or self.not_comment == False \
            or self.single_not_comment == False \
            or self.setting_flags == True:
            if cur_line == '': 
                token = Word(0, '$', '$')
                token_code = 1
                break
            cur_char = cur_line[cur_line_pointer]                   # get next character in read buffer
            cur_line_pointer +=1
            if cur_char == ' ' or  cur_char == '\t':
                pass                            # strip out white space
            elif cur_char == '\n':
                cur_line_pointer = 0
                self.lineno += 1                # increment lineno
                self.single_not_comment = True
                if self.flag1 == True:
                    print cur_line[:-1]
                cur_line = pgmLines.readline()
                pass
            elif cur_char.isdigit():                   # get number
                cur_line_pointer +=-1
                num = find_num()
                allowed_len = 7
                if num.count('.'): allowed_len = 10
                if allowed_len < 9 and not num.count('.'):
                    if len(num) > allowed_len:
                        token_code = 300
                        tag = 'Invalid integer'
                        self.produce(num, token_code)
                    else:
                        token_code = 48
                        tag = 'integer'
                elif len(num) < allowed_len and num.count('.'):
                    token_code = 47
                    tag = 'real'
                else:
                    token_code = 300
                    tag = 'Invalid real'
                    self.produce(num, token_code)
                token = Word(token_code, num, tag)       
            elif cur_char.isalpha() \
                    and cur_char.islower():  
                cur_line_pointer +=-1                
                identifier = find_identifier()
                if len(identifier) < 17:
                    token_code = 21
                    tag = 'var'
                else:
                    token_code = 301
                    tag = 'Invalid var'
                    self.produce(identifier, token_code)
                token = Word(token_code, identifier, tag)
            elif cur_char.isalpha() \
                    and cur_char.isupper():                  
                cur_line_pointer +=-1
                reserved = find_reserved()
                token_code = self.lookup(reserved) 
                if not token_code:
                    token_code = 303
                    tag = "Invalid Reserved"
                    self.produce(reserved, token_code)
                else: tag = reserved
                token = Word(token_code, reserved, tag)
            elif cur_char == EOF:
                token = Token(260)
            else:
                next_char = cur_line[cur_line_pointer]
                if next_char != '\n':
                    next_next_char = cur_line[cur_line_pointer+1]
                if next_char > ' ':
                    token_code = self.lookup(cur_char + next_char) 
                    if token_code:
                         token = Word(token_code, cur_char + next_char, cur_char + next_char)
                         cur_line_pointer +=1
                    elif cur_char == '\"':
                        cur_line_pointer +=-1
                        this_literal = find_literal()
                        if this_literal:
                            token_code = 53
                            tag = 'string'
                        else:
                            token_code = 305
                            'Invalid string'
                            self.produce(this_literal, token_code)
                        token = Word(token_code, this_literal,tag)
                    elif cur_char == '/' and next_char == '*' \
                            and self.not_comment == True:
                        self.not_comment = False
                        cur_line_pointer +=1
                        token = ''
                    elif cur_char == '*' and next_char == '/' \
                            and self.not_comment == False:
                        self.not_comment = True
                        cur_line_pointer +=1
                        token = ''
                    elif cur_char == '/' and next_char == '/':
                        self.single_not_comment = False
                        cur_line_pointer +=1
                        token = ''
                    elif cur_char + next_char == '##' \
                            and self.setting_flags == False:
                        self.setting_flags = True
                        cur_line_pointer +=1
                        token = ''
                    elif cur_char + next_char == '##' \
                            and self.setting_flags == True:
                        self.setting_flags = False
                        cur_line_pointer +=1
                        token = ''
                    elif cur_char == '+' and next_char.isdigit() and next_next_char.isdigit() \
                            and self.setting_flags == True \
                            and self.not_comment == True \
                            and self.single_not_comment == True:
                        cur_line_pointer +=2
                        if (next_char == '1' and next_next_char == '0') or next_char == '0': self.flag10 = True
                        if (next_char == '1' and next_next_char == '1') or next_char == '0': self.flag11 = True
                        if (next_char == '1' and next_next_char == '2') or next_char == '0': self.flag12 = True
                        if (next_char == '1' and next_next_char == '3') or next_char == '0': self.flag13 = True
                        if (next_char == '1' and next_next_char == '4') or next_char == '0': self.flag14 = True
                        if (next_char == '1' and next_next_char == '5') or next_char == '0': self.flag15 = True
                        if (next_char == '1' and next_next_char == '6') or next_char == '0': self.flag16 = True
                        if (next_char == '1' and next_next_char == '7') or next_char == '0': self.flag17 = True
                        if (next_char == '1' and next_next_char == '8') or next_char == '0': self.flag18 = True 
                        if (next_char == '1' and next_next_char == '9') or next_char == '0': self.flag19 = True            
                    elif cur_char == '+' and next_char.isdigit() \
                            and self.setting_flags == True \
                            and self.not_comment == True \
                            and self.single_not_comment == True:
                        cur_line_pointer +=1
                        if next_char == '1' or next_char == '0': self.flag1 = True
                        if next_char == '2' or next_char == '0': self.flag2 = True
                        if next_char == '3' or next_char == '0': self.flag3 = True
                        if next_char == '4' or next_char == '0': self.flag4 = True
                        if next_char == '5' or next_char == '0': self.flag5 = True
                        if next_char == '6' or next_char == '0': self.flag6 = True
                        if next_char == '7' or next_char == '0': self.flag7 = True
                        if next_char == '8' or next_char == '0': self.flag8 = True 
                        if next_char == '9' or next_char == '0': self.flag9 = True
                    elif cur_char == '-' and next_char.isdigit() and next_next_char.isdigit() \
                            and self.setting_flags == True \
                            and self.not_comment == True \
                            and self.single_not_comment == True:
                        cur_line_pointer +=2
                        if (next_char == '1' and next_next_char == '0') or next_char == '0': self.flag10 = False
                        if (next_char == '1' and next_next_char == '1') or next_char == '0': self.flag11 = False
                        if (next_char == '1' and next_next_char == '2') or next_char == '0': self.flag12 = False
                        if (next_char == '1' and next_next_char == '3') or next_char == '0': self.flag13 = False
                        if (next_char == '1' and next_next_char == '4') or next_char == '0': self.flag14 = False
                        if (next_char == '1' and next_next_char == '5') or next_char == '0': self.flag15 = False
                        if (next_char == '1' and next_next_char == '6') or next_char == '0': self.flag16 = False
                        if (next_char == '1' and next_next_char == '7') or next_char == '0': self.flag17 = False
                        if (next_char == '1' and next_next_char == '8') or next_char == '0': self.flag18 = False 
                        if (next_char == '1' and next_next_char == '9') or next_char == '0': self.flag19 = False
                    elif cur_char == '-' and next_char.isdigit() \
                            and self.setting_flags == True \
                            and self.not_comment == True \
                            and self.single_not_comment == True:
                        cur_line_pointer +=1
                        if next_char == '1' or next_char == '0': self.flag1 = False
                        if next_char == '2' or next_char == '0': self.flag2 = False
                        if next_char == '3' or next_char == '0': self.flag3 = False
                        if next_char == '4' or next_char == '0': self.flag4 = False
                        if next_char == '5' or next_char == '0': self.flag5 = False
                        if next_char == '6' or next_char == '0': self.flag6 = False
                        if next_char == '7' or next_char == '0': self.flag7 = False
                        if next_char == '8' or next_char == '0': self.flag8 = False 
                        if next_char == '9' or next_char == '0': self.flag9 = False
                    elif (cur_char == '-') and next_char.isdigit() \
                                and self.setting_flags == False:
                            cur_line_pointer +=-1
                            cur_line_pointer +=-1
                            num = find_num()
                            allowed_len = 7
                            if num.count('.'): allowed_len = 10
                            if num.count('-'):
                                 allowed_len += 1
                            if allowed_len < 9 and not num.count('.'):
                                token_code = 48
                            elif len(num) < allowed_len and num.count('.'):
                                token_code = 47
                            else:
                                token_code = 300
                            token = Word(token_code, num)
                    elif not token_code:
                        cur_char = cur_line[cur_line_pointer-1]
                        token_code = self.lookup(cur_char)
                        if not token_code:
                            token = Token(cur_char)
                        else:
                            token = Word(token_code, cur_char, cur_char)
                else:
                    token_code = self.lookup(cur_char)
                    cur_char = cur_line[cur_line_pointer-1]
                    if not token_code:
                        token = Token(cur_char)
                    else:
                        token = Word(token_code, cur_char, cur_char)
        if token_code:
            return token
        else:
            print token
   
    def produce(self, value, token_code):
        if self.not_comment == False \
            or self.single_not_comment == False \
            or self.setting_flags == True:
                pass
        else:          

           out = value
      
           if token_code == 300:
                if out.count('.'):
                    p = 'Invalid real %s The number has more than 7 significant digits.' % (out)
                else:
                    p = 'Invalid integer %s The number has more than 9 significant digits.' % (out)
           elif token_code == 301:
                p = 'Invalid identifier %s' % (out)
           elif token_code == 303:
                p = 'Invalid reserved word %s' % (out)
           elif token_code == 305:
                p = 'Invalid literal %s' % (out)
           elif token_code == '' or token_code == None:
                    p = 'Invalid ASCII character %s ASCII value is %s' % (out,ord(out))
           print p.rjust(80)

           if token_code == 301:
                print ' Identifiers should be at most 16 characters of the form [a-z]([a-z]|[0-9]|-|_)*'

   
       

